#include "csapp.h"
#include "sbuf.h"

/* Recommended max cache and object sizes */
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400

/* You won't lose style points for including this long line in your code */
static const char *user_agent_hdr = "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3) Gecko/20120305 Firefox/10.0.3\r\n";

void doit(int clientfd);
int parse_uri(char *uri,char *hostname,char *path,char *port,char *request_head);
void read_requesthdrs(rio_t *rp,int fd);
void return_content(int serverfd, int clientfd);


int main(int argc,char **argv)
{
    int listenfd,connfd;
    char hostname[MAXLINE],port[MAXLINE];
    socklen_t clientlen;
    struct sockaddr_storage clientaddr;

    if(argc != 2){
        fprintf(stderr, "usage: %s <port>\n", argv[0]);
        exit(1);
    }

    /* 代理创建一个监听描述符，准备好接收连接请求 */
    listenfd = open_listenfd( atoi(argv[1]) );
    while(1){
        clientlen = sizeof(clientaddr);

        /* 等待来着客户端的连接请求到侦听描述符listenfd，
        然后在addr中填写客户端的套接字地址，并返回一个已连接描述符 
        */
        connfd = Accept(listenfd,(SA *)&clientaddr,&clientlen);

        /* 将套接字地址结构clientaddr转化成对应的主机和服务名字符串，
        并将他们复制到hostname和port缓冲区
         */
        getnameinfo((SA *)&clientaddr,clientlen,hostname,MAXLINE,port,MAXLINE,0);
        printf("Accepted connection from (%s, %s)\n",hostname,port);
        doit(connfd);
        Close(connfd);
    }
}

/* 处理客户端HTTP事务 */
void doit(int clientfd){

    char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE];
    char hostname[MAXLINE],path[MAXLINE],port[MAXLINE],request_head[MAXLINE];
    int serverfd;
    rio_t rio;

    /* 读取请求行和请求头 */
    Rio_readinitb(&rio,clientfd);
    Rio_readlineb(&rio,buf,MAXLINE);
    sscanf(buf,"%s %s %s",method,uri,version);
    if(strcasecmp(method,"GET")){
        printf("Not implemented");
        return;
    }

    /* 解析uri取hostname和path和port。生成request_head */
    parse_uri(uri,hostname,path,port,request_head);

    /* 建立与服务器的连接 */
    serverfd = Open_clientfd(hostname,port);

    /* 将请求头传递给服务器 */
    Rio_writen(serverfd,request_head,strlen(request_head));
    read_requesthdrs(&rio,serverfd);

    /* 将服务端读取的数据返回给客户端 */
    return_content(serverfd,clientfd);


}

/*  解析uri中的hostname和path和port。并生成request_head
 *  uri例子：http://www.cmu.edu:8080/hub/index.html
 *  hostname:www.cmu.edu
 *  path:/hub/index.html
 *  port:8080
 */
int parse_uri(char *uri,char *hostname,char *path,char *port,char *request_head){

    sprintf(port,"13345");     //默认值

    char *end,*bp;
    char *tail = uri+strlen(uri);   //uri的最后一个字符，不是'\0'。

    char *bg = strstr(uri,"//");

    bg = (bg!=NULL ? bg+2 : uri);   //取hostname的开头。

    end = bg;                        
    //取hostname的结尾。
    while(*end != '/' && *end != ':') end++;
    strncpy(hostname,bg,end-bg);

    bp = end + 1;                   //取port的开头
    if(*end == ':'){                //==':'说明uri中有port
        end++;
        bp = strstr(bg,"/");        //取port的结尾

        strncpy(port,end,bp-end);   
        end = bp;                   //取uri的开头
    }
    strncpy(path,end,(int)(tail-end)+1);

    /* 请求的开头行：GET /hub/index.html HTTP/1.0。 */
    sprintf(request_head,"GET %s HTTP/1.0\r\nHost: %s\r\n",path,hostname);
    
    return 1;
}

/*
 *  读取HTTP请求头
 *  Host,User-Agent,Connection和Proxy-Connection用指定的的
 *  保留其他的头(header)
 */
void read_requesthdrs(rio_t *rp,int fd){
    
    char buf[MAXLINE];

    sprintf(buf, "%s", user_agent_hdr);
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Connection: close\r\n");
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Proxy-Connection: close\r\n");
    Rio_writen(fd, buf, strlen(buf));

    /* 保留其他的头(header) */
    for(Rio_readlineb(rp,buf,MAXLINE);strcmp(buf,"\r\n");Rio_readlineb(rp,buf,MAXLINE)){
        if(strncmp("Host",buf,4) == 0 || strncmp("User-Agent",buf,10) == 0
            || strncmp("Connection",buf,10) == 0 || strncmp("Proxy-Connection",buf,16) == 0)
                continue;
        printf("%s",buf);
        Rio_writen(fd,buf,strlen(buf));
    }
    Rio_writen(fd,buf,strlen(buf));
    return;
}

/*
 * 将服务端读取的数据返回给客户端
 */
void return_content(int serverfd, int clientfd){

    size_t n;
    char buf[MAXLINE];
    rio_t srio;

    Rio_readinitb(&srio,serverfd);
    while((n = Rio_readlineb(&srio,buf,MAXLINE)) != 0){
        Rio_writen(clientfd,buf,n);
    }
    
}

